import { $set } from '@feng3d/serialization';
import { AnimationMixer3D, Clock, Color, Fog3D, MeshPhongMaterial3D, Node3D, PerspectiveCamera3D, PlaneGeometry3D, Scene3D, WebGLRenderer3D } from 'feng3d-three';
import { GLTFLoader3D } from '../../extension/loaders/GLTFLoader3D';
import { SkeletonUtils3D } from '../../extension/utils/SkeletonUtils3D';

let camera: PerspectiveCamera3D;
let scene: Scene3D;
let renderer: WebGLRenderer3D;
let clock: Clock;

const mixers: AnimationMixer3D[] = [];

init();
animate();

function init()
{
    camera = new Node3D().addComponent('PerspectiveCamera3D', { fov: 45, aspect: window.innerWidth / window.innerHeight, near: 1, far: 1000 });
    camera.entity.position.set(2, 3, -6);
    camera.entity.lookAt(0, 1, 0);

    clock = new Clock();

    scene = new Node3D().addComponent('Scene3D');
    scene.background = new Color(0xa0a0a0);
    scene.fog = new Fog3D(0xa0a0a0, 10, 50);

    const hemiLight = new Node3D().addComponent('HemisphereLight3D', { color: { value: 0xffffff }, groundColor: { value: 0x444444 } });
    hemiLight.entity.position.set(0, 20, 0);
    scene.entity.addChild(hemiLight.entity);

    const dirLight = new Node3D().addComponent('DirectionalLight3D', { color: { value: 0xffffff } });
    dirLight.entity.position.set(-3, 10, -10);
    dirLight.castShadow = true;
    dirLight.shadow.camera.top = 4;
    dirLight.shadow.camera.bottom = -4;
    dirLight.shadow.camera.left = -4;
    dirLight.shadow.camera.right = 4;
    dirLight.shadow.camera.near = 0.1;
    dirLight.shadow.camera.far = 40;
    scene.entity.addChild(dirLight.entity);

    const cameraHelper = new Node3D().addComponent('CameraHelper3D', { camera: dirLight.shadow.camera });

    scene.entity.addChild(cameraHelper.entity);

    // ground

    const mesh = new Node3D().addComponent('Mesh3D', {
        geometry: $set(new PlaneGeometry3D(), { width: 200, height: 200 }),
        material: $set(new MeshPhongMaterial3D(), { color: { value: 0x999999 }, depthWrite: false }),
    });
    mesh.entity.rotation.x = -Math.PI / 2;
    mesh.receiveShadow = true;
    scene.entity.addChild(mesh.entity);

    const loader = new GLTFLoader3D();
    loader.load('../../models/gltf/Soldier.glb', function (gltf)
    {
        gltf.scene._group.traverse(function (object: any)
        {
            if (object.isMesh) object.castShadow = true;
        });

        const model1 = SkeletonUtils3D.clone(gltf.scene);
        const model2 = SkeletonUtils3D.clone(gltf.scene);
        const model3 = SkeletonUtils3D.clone(gltf.scene);

        const mixer1 = new AnimationMixer3D(model1);
        const mixer2 = new AnimationMixer3D(model2);
        const mixer3 = new AnimationMixer3D(model3);

        mixer1.clipAction(gltf.animations[0]).play(); // idle
        mixer2.clipAction(gltf.animations[1]).play(); // run
        mixer3.clipAction(gltf.animations[3]).play(); // walk

        model1.position.x = -2;
        model2.position.x = 0;
        model3.position.x = 2;

        scene.entity.addChild(model1);
        scene.entity.addChild(model2);
        scene.entity.addChild(model3);
        mixers.push(mixer1, mixer2, mixer3);

        animate();
    });

    renderer = scene.addComponent('WebGLRenderer3D', { parameters: { antialias: true } });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.outputEncoding = 'sRGBEncoding';
    renderer.shadowMap.enabled = true;
    document.body.appendChild(renderer.domElement);

    window.addEventListener('resize', onWindowResize);
}

function onWindowResize()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate()
{
    requestAnimationFrame(animate);

    const delta = clock.getDelta();

    for (const mixer of mixers) mixer.update(delta);

    renderer.render(scene, camera);
}
